{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Tool-urile de bază pentru Private Deep Learning\n",
    "\n",
    "Bine ați venit la tutorialul introductiv în PySyft, un tool pentru deep learning descentralizat, pentru păstrarea anonimității datelor. Acestă serie de notebook-uri este un ghid pas-cu-pas care își propune să vă familiarizeze cu noile tool-uri și tehnici necesare pentru a face deep learning pe date/modele secrete/private făra ca acestea să fie deținute de o autoritate.\n",
    "\n",
    "**Notă:** Nu vom vorbi doar despre descentralizarea / criptarea datelor, dar vom vedea și cum PySyft poate fi folosit pentru a ajuta la descentralizara întregului ecosistem din jurul datelor, incluzând chiar și bazele de date (unde datele sunt stocate și interogate), rețelele neuronale care sunt folosite să extragem informații din date. Pe măsura ce noi extensii vor fi create pentru PySyft, aceste notebook-uri vor fi extinse cu noi tutoriale pentru a explica noua funcționalitate.\n",
    "\n",
    "Autor:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Traducător:\n",
    "- George Muraru: Github [@gmuraru](https://github/com/gmuraru) | Twitter: [@georgemuraru](https://twitter.com/georgemuraru) | Facebook: [@George Cristian Muraru](https://www.facebook.com/georgecmuraru)\n",
    "\n",
    "\n",
    "## Outline:\n",
    "\n",
    "- Partea 1: Tool-uri de bază pentru Private Deep Learning\n",
    "\n",
    "\n",
    "## De ce să faci acest tutorial?\n",
    "\n",
    "**1) Un avantaj în carieră** - În ultimi 20 de ani, evoluția digitală a făcut ca datele sa fie din ce în ce mai accesibile, Cu toate acestea, cu reglementări precum [GDPR](https://eugdpr.org/), corporațiile se află sub presiune să nu mai puțină libertate cu modul în care folosesc - și mai important despre cum analizează - datele personale. **Concluzie:** Persoanele care lucrează în domeniul Data Science nu vor putea avea acces la fel de multe date cu tool-uri \"de modă veche\", însă învățând tool-urile de Private Deep Learning, TU poți avea un avantajat competitiv în carieră.\n",
    "**2) Oportunități antreprenoriale** - Există o serie întreagă de probleme în societate pe care Deep Learnig-ul le poate soluționa, dar multe dintre acestea nu au fost explorate deoarece ar necesita accesul la informații foarte sensibile despre oameni (consideră că ai folosi Deep Learning pentru a ajuta persoane cu probleme mentale sau probleme în relație!). Având în vedere aceste lucruri, învățând despre Private Deep Learning scoate la lumină o noua mulțime de oportunități de startup care nu erau valabile înainte fără aceste tool-uri.\n",
    "**3) Binele social** - Deep Learning poate fi folosit pentru rezolvarea unei game variate de probleme în viața reală, însă aplicarea metodelor Deep Learning pe *informații personale* este Deep Learning despre oameni, *pentru oameni*. Învățând cum să aplici Deep Learning pe date nedeținute \n",
    "\n",
    "## Cum primesc extra credit?\n",
    "\n",
    "- Oferă o \"steluță\" PySyft on GitHub! - [https://github.com/OpenMined/PySyft](https://github.com/OpenMined/PySyft)\n",
    "- Crează un video pe Youtube Make a Youtube video teaching this notebook!\n",
    "\n",
    "\n",
    "... ok ... hai să trecem la treaba!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Partea -1: Cerințe preliminare\n",
    "\n",
    "- Cunosți PyTorch - dacă nu, poți lua cursul http://fast.ai, iar după te poți întoarce la acest notebook\n",
    "- Citește paper-ul despre Framework-ul de PySyft https://arxiv.org/pdf/1811.04017.pdf! Acest lucru iți va oferi un background despre cum este construit PySyft și va face ca lucrurile să aibă mai mult sens."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 0: Setup\n",
    "\n",
    "Pentru a începe, trebuie să te asiguri că ai instalate pachetele necesare. Pentru a face acest lucru, mergi la README-ul PySyft și urmărește instrucțiunile de setup. TLDR pentru majoritatea.\n",
    "To begin, you'll need to make sure you have the right things installed. To do so, head on over to PySyft's readme and follow the setup instructions. TLDR for most folks is.\n",
    "\n",
    "- Install Python 3.5 or higher\n",
    "- Install PyTorch 1.1\n",
    "- Clone PySyft (git clone https://github.com/OpenMined/PySyft.git)\n",
    "- cd PySyft\n",
    "- pip install -r pip-dep/requirements.txt\n",
    "- pip install -r pip-dep/requirements_udacity.txt\n",
    "- python setup.py install udacity\n",
    "- python setup.py test\n",
    "\n",
    "Dacă nu merge o anumită parte din tutorial (sau pica vreun test) - prima dată verifică [README-ul](https://github.com/OpenMined/PySyft.git) pentru ajutor, iar după deschide un Issue pe GitHub sau dă un ping pe canalul de #beginner de pe Slack! [slack.openmined.org](http://slack.openmined.org/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Falling back to insecure randomness since the required custom op could not be found for the installed version of TensorFlow. Fix this by compiling custom ops. Missing file was '/home/george/.conda/envs/pysyft-contrib/lib/python3.7/site-packages/tf_encrypted/operations/secure_random/secure_random_module_tf_1.15.0.so'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /home/george/.conda/envs/pysyft-contrib/lib/python3.7/site-packages/tf_encrypted/session.py:24: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n",
      "\n"
     ]
    },
    {
     "ename": "UndefinedProtocolTypeError",
     "evalue": "syft.frameworks.torch.tensors.interpreters.private.PrivateTensor is not defined in the protocol file",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUndefinedProtocolTypeError\u001b[0m                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-1-c5d7ccb73f59>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfunctional\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mF\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0msyft\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0msy\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m \u001b[0mhook\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTorchHook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/__init__.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     77\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     78\u001b[0m \u001b[0;31m# Import serialization tools\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 79\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mserde\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     80\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     81\u001b[0m \u001b[0;31m# import functions\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/serde/__init__.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtorch_serde\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0m_simplify\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0m_detail\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msyft\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserde\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0m_compress\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/serde/serde.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m    250\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 252\u001b[0;31m \u001b[0msimplifiers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mforced_full_simplifiers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetailers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_generate_simplifiers_and_detailers\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    253\u001b[0m \u001b[0;31m# Store types that are not simplifiable (int, float, None) so we\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    254\u001b[0m \u001b[0;31m# can ignore them during serialization.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/serde/serde.py\u001b[0m in \u001b[0;36m_generate_simplifiers_and_detailers\u001b[0;34m()\u001b[0m\n\u001b[1;32m    240\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0msyft_type\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mOBJ_SIMPLIFIER_AND_DETAILERS\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mEXCEPTION_SIMPLIFIER_AND_DETAILERS\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    241\u001b[0m         \u001b[0msimplifier\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetailer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msyft_type\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msimplify\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msyft_type\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdetail\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 242\u001b[0;31m         \u001b[0m_add_simplifier_and_detailer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msyft_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msimplifier\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetailer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    243\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    244\u001b[0m     \u001b[0;31m# Register syft objects with custom force_simplify and force_detail methods\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/serde/serde.py\u001b[0m in \u001b[0;36m_add_simplifier_and_detailer\u001b[0;34m(curr_type, simplifier, detailer, forced)\u001b[0m\n\u001b[1;32m    224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    225\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m_add_simplifier_and_detailer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcurr_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msimplifier\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetailer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mforced\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 226\u001b[0;31m         \u001b[0mtype_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mproto_type_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcurr_type\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    227\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mforced\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    228\u001b[0m             \u001b[0mforced_full_simplifiers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mcurr_type\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtype_info\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mforced_code\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msimplifier\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/contrib/PySyft/syft/serde/proto.py\u001b[0m in \u001b[0;36mproto_type_info\u001b[0;34m(cls)\u001b[0m\n\u001b[1;32m     68\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mTypeInfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtype_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mproto_info\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"TYPES\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mtype_name\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     69\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 70\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mUndefinedProtocolTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"%s is not defined in the protocol file\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mtype_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mUndefinedProtocolTypeError\u001b[0m: syft.frameworks.torch.tensors.interpreters.private.PrivateTensor is not defined in the protocol file"
     ]
    }
   ],
   "source": [
    "# Run this cell to see if things work\n",
    "import sys\n",
    "\n",
    "import torch\n",
    "from torch.nn import Parameter\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "torch.tensor([1,2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dacă celula precedentă s-a executat, atunci totul este în regulă! Putem sa continuăm tutorialul."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Partea 1: Tool-urile de baza pentru Data Science Privat, Descentralizat\n",
    "\n",
    "Prima întrebare care poate să apară - Cum putem să antrenăm un model pe date la care nu avem accces?\n",
    "Răspunsul este surprinzător de simplu. Dacă ești obișnuit să lucrezi în PyTorch, atunci vei fi obișnuit să lucrezi cu obiecte de tipul torch.Tensor în modul în care vom vedea în acest tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([ 2,  4,  6,  8, 10])\n"
     ]
    }
   ],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = x + x\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evident, folosirea acestor tensori extravaganți (și puternici!) este important, dar trebuie să ai datele și pe mașina locală. Aici începe călătoria noastră.\n",
    "\n",
    "# Secțiunea 1.1 - Trimiterea de Tensori spre mașina lui Bob\n",
    "\n",
    "În mod normal am efectua tehnici de data science / deep learning pe mașina care deține datele, acum dorim să efectuăm acest tip de calcul pe **altă** mașină. Mai specific, nu mai putem face presupunerea că datele sunt pe mașina noastră locală.\n",
    "\n",
    "Prin urmare, în loc să folosim tensorii din Torch, vom folosi **pointeri** spre tensori. Lasă-mă să îți arăt ce vreau să spun. Prima data, hai să creăm o \"presupusă\" mașină deținută de o \"presupusă\" persoană - se va numi Bob."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'sy' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-3-b9e1037deafb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mbob\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mVirtualWorker\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhook\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"bob\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'sy' is not defined"
     ]
    }
   ],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hai să zicem că mașina lui Bob este pe o altă planetă - poate pe Marte! Dar, în acest moment, mașina nu conține informații. Vom crea niște date astfel încât le vom trimite lui Bob și vom învăța despre pointeri!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = torch.tensor([1,1,1,1,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Și acum - să trimitem tensorii lui Bob!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr = x.send(bob)\n",
    "y_ptr = y.send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BOOM! Acum Bob are doi tensori! Nu mă crezi? Aruncă o privire!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x_ptr + x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Acum observă. Când am apelat `x.send(bob)` a întors un nou obiect pe care l-am numit `x_ptr`. Acesta este primul nostru *pointer* la un tensor. Pointerii la tensori NU conțin date efective. În loc, ei conțin doar metadate despre un tensor (cu anumite date) stocat pe o altă mașină. Scopul acestor tensori este de a ne oferi un API intuitiv \n",
    "\n",
    "Now notice something. When we called `x.send(bob)` it returned a new object that we called `x_ptr`. This is our first *pointer* to a tensor. Pointers to tensors do NOT actually hold data themselves. Instead, they simply contain metadata about a tensor (with data) stored on another machine. The purpose of these tensors is to give us an intuitive API to tell the other machine to compute functions using this tensor. Let's take a look at the metadata that pointers contain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Check out that metadata!\n",
    "\n",
    "Sunt două atribute principale specifice pointerilor:\n",
    "\n",
    "- `x_ptr.location : bob`, locația, o referință the location, a reference to the location that the pointer is pointing to\n",
    "- `x_ptr.id_at_location : <random integer>`, id-ul  where the tensor is stored at location\n",
    "\n",
    "Sunt afișați în următorul format `<id_at_location>@<location>`\n",
    "\n",
    "Există și alte atribute mai generice:\n",
    "- `x_ptr.id : <random integer>`, id-ul pointer-ului la tensor, a fost alocat random\n",
    "- `x_ptr.owner : \"me\"`, worker-ul (vom folosi cuvântul worker pentru astfel de termeni) care deține pointer-ul la tensor, aici este worker-ul local, numit \"me\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob == x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.id_at_location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Poate te întrebi de ce worker-ul local, care deține pointerul, este de asemenea un VirtualWorker, chiar daca nu l-am creat. Fun fact, la fel cum aveam un obiect de tipul VirtualWorker pentru Bob, mereu vom avea (în mod implicit) un VirtualWorker și pentru noi. Acest worker este creat automat când apelăm `hook = sy.TorchHook()` și astfel nu trebuie să îl creezi explciit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "me = sy.local_worker\n",
    "me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "me == x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Și în final, la fel cum putem apela .send() pe un tensor, putem apela .get() pe un pointer la un tensor pentru a obține tensorul înapoi!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Și după cum poți vedea... Bob nu mai deține tensorii!!! Au fost mutați înapoi pe mașina noastră!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Secțiunea 1.2 - Folosirea Pointerilor spre Tensori\n",
    "\n",
    "Așadar, trimiterea și primirea tensorilor de la Bob este grozavă, dar acestui lucru i se poate spune cu greu Deep Learning! Dorim să putem face _operații_ de tensori, pe tensori aflați la distanță (pe alte mașini). Din fericire, pointerii spre tensori fac acest lucru destul de ușor! Poți folosi pointerii excat ca și cum ai folosi tensori normali!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5]).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x + y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Și voilà! \n",
    "\n",
    "În spatele scenei, un lucru interesant s-a întâmplat. În loc ca x și y să , a fost serializată o comandă și a fost trimisă lui Bob, care a efectuat calculul, a creat un tensor z, și mai apoi a întors un pointer spre z!\n",
    "\n",
    "Dacă apelăm .get() pe pointer, vom avea rezultatul operației pe mașina noastră!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Funcții Torch\n",
    "\n",
    "API-ul a fost extins astfel încât să suporte toate operațiile din Torch!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = torch.add(x,y)\n",
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variabile (inclusiv backpropagation!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5.], requires_grad=True).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1.], requires_grad=True).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = (x + y).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "După cum se poate vedea, API-ul este foarte flexibil și capabil să efectueze aproape toate operațiile pe care le-ai efectua în Torch pe *date aflate la distanță*. Acest lucru pune bazele pentru protocoalele noastre mai avansate de privacy preserving precum Federated Learning, Secure Multi-Party Computation și Differential Privacy!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Felicitări!!! - Este timpul să te alături comunității!\n",
    "\n",
    "Felicitări pentru completarea tutorialului! Daca ți-a făcut plăcere și ai dori să faci parte din \n",
    "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the movement toward privacy preserving, decentralized ownership of AI and the AI supply chain (data), you can do so in the following ways!\n",
    "\n",
    "### Oferă o stea repo-ului PySyft pe GitHub\n",
    "\n",
    "Cea mai ușoară metodă de a ajuta comunitatea este de a oferi o \"steluță\" repo-urilor de pe GitHub! Acest lucru ajută la acumularea de notorietate în ceea ce privesc tool-urile cool la care lucrăm.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Alaturăte comunității pe Slack!\n",
    "\n",
    "Cea mai bună metodă de a fi la curent cu ultimele progrese este să te alături comunității noastre. Poți face acest lucru prin completarea formularului de la [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Alătură-te unui proiect!\n",
    "\n",
    "Cea mai bună metodă de a contribui este de a deveni un contributor activ (de a scrie cod și crea pull request-uri - PR-uri). În orice moment poți merge pe pagina GitHub, cu Issues, a proiectului, și să filtrezi după \"Projects\". Asta îți va arăta Ticketele \"generale\" și îți va oferi o privire de ansamblu despre proiectele la care poți participa. Dacă nu dorești să te alături unui proiect, dar dorești să scrii câteva linii de cod, poți să cauți \"mini-proiecte\" prin căutarea tagului de \"good first issue\".\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donează\n",
    "\n",
    "Dacă nu ai timp să contribui la codebase, dar dorești să îți arăți suportul, se poate, de asemenea, să devii un Backer pentru Open Collective. Toate donațiile merg spre hosting-ului paginii web și alte cheltuieli ale comunității, cum ar fi hackathoane și întâlniri!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
